var scene,
  camera,
  renderer,
  group,
  labelRenderer,
  role,
  roleTalkTarget,
  raycaster,
  mixer,
  material,
  action;
var actionState = "";
var roleAnimations = [];
var timer = null;
var ASSETSURL = "../assets/06/"; // 资源主目录路径
var clock = new THREE.Clock();
var w = window.innerWidth,
  h = window.innerHeight;
var size = 0.201;
var mouse = new THREE.Vector2();
var INTERSECTED = null,
  SELECTITEM = null;
var point = null;
var rolePoint = { x: 0, z: 5 };
var speed = 0.4;
var isMove = false;
var row = 20,
  col = 20;
var spotLight;
var animatinFinishedEventList = [];
var giftList = [];
var isOpenBag = false;
var giftData = {
  note: {
    name: "前端笔记",
    pic: "face.png",
    url: "https://jsmask.gitee.io/note_blob/",
    desc: "一份神秘的笔记，貌似会有很多发现在里面",
  },
  codepen: {
    name: "codepen",
    pic: "codepen.jpg",
    url: "https://codepen.io/jsmask",
    desc: "好多新奇的效果与创意",
  },
  gitee: {
    name: "gitee",
    pic: "gitee.jpg",
    url: "https://gitee.com/jsmask",
    desc: "上面好像记载了码云..",
  },
  github: {
    name: "github",
    pic: "github.jpg",
    url: "https://github.com/jsmask",
    desc: "真不敢相信，记载了好多挖掘方案..",
  },
  svgatopngs: {
    name: "svgatopngs",
    pic: "svgatopngs.jpg",
    url: "https://jsmask.github.io/svgatopngs",
    desc: "没什么新奇，应该是个简单的在线格式转换工具",
  },
};
var itemsMap = [
  {
    x: -6,
    z: -1,
    type: "braiser",
    name: "石匣",
    label: true,
    gift: ["codepen"],
  },
  {
    x: 5,
    z: -1,
    type: "braiser",
    name: "石匣",
    label: true,
    gift: ["note"],
  },
  {
    x: 5,
    z: 4,
    type: "pillar",
    name: "石柱",
    label: false,
    gift: [],
  },
  {
    x: -6,
    z: 4,
    type: "pillar",
    name: "石柱",
    label: false,
    gift: [],
  },
  {
    x: 5,
    z: -5,
    type: "pillar",
    name: "石柱",
    label: false,
    gift: [],
  },
  {
    x: -6,
    z: -5,
    type: "pillar",
    name: "石柱",
    label: false,
    gift: [],
  },
  {
    x: 9,
    z: 9,
    type: "barrel_none",
    name: "空木桶",
    label: true,
    gift: [],
  },
  {
    x: 9,
    z: 8,
    type: "barrel_bad",
    name: "破碎的木桶",
    label: true,
    gift: [],
  },
  {
    x: 9,
    z: 6,
    type: "barrel_normal",
    name: "木桶",
    label: true,
    gift: [],
  },
  {
    x: 9,
    z: -10,
    type: "barrel_bad",
    name: "破碎的木桶",
    label: true,
    gift: [],
  },
  {
    x: 8,
    z: -10,
    type: "barrel_none",
    name: "空木桶",
    label: true,
    gift: [],
  },
  {
    x: -9,
    z: -10,
    type: "barrel_none",
    name: "空木桶",
    label: true,
    gift: [],
  },
  {
    x: -10,
    z: -10,
    type: "barrel_normal",
    name: "木桶",
    label: true,
    gift: ["gitee", "github"],
  },
  {
    x: -8,
    z: 5,
    type: "stone",
    name: "石块",
    label: true,
    gift: ["svgatopngs"],
  },
  {
    x: 5,
    z: 5,
    type: "stone",
    name: "石块",
    label: true,
    gift: [],
  },
  {
    x: 7,
    z: -7,
    type: "stone",
    name: "石块",
    label: true,
    gift: [],
  },
];

var initUiEvent = (function () {
  var createApp = PetiteVue.createApp;

  createApp({
    list: [],
    isOpen: false,
    openBag() {
      this.isOpen = isOpenBag = true;
      this.list.length = 0;
      giftList.forEach(function (item) {
        var obj = JSON.parse(JSON.stringify(item));
        obj.pic = ASSETSURL + "imgs/" + item.pic;
        this.list.push(obj);
      }.bind(this));
      point.visible = false;
    },
    closeBag() {
      this.isOpen = false
      setTimeout(function () {
        isOpenBag = false;
        point.visible = true;
      },300);
    },
    toLink(item){
      window.open(item.url,"_blank")
    }
  }).mount("#ui");
})();

(function () {
  initVariable();
  addLight();
  loadModel();
  run();
  // new THREE.OrbitControls(camera, renderer.domElement);
})();

function initVariable() {
  // 创建摄像机
  camera = new THREE.PerspectiveCamera(
    1,
    document.body.clientWidth / document.body.clientHeight,
    1,
    10000
  );

  camera.position.x = 60;
  camera.position.z = 60;
  camera.position.y = 60;

  // 初始化场景
  scene = new THREE.Scene();
  scene.fog = new THREE.FogExp2(0x040455, 0.003);

  group = new THREE.Group();
  scene.add(group);

  // 初始化renderer
  renderer = new THREE.WebGLRenderer({
    // antialias: true,
    // alpha: true,
  });
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setClearColor(new THREE.Color(0x030303));
  document.getElementById("container").appendChild(renderer.domElement);

  addPoint();

  labelRenderer = new THREE.CSS2DRenderer();
  labelRenderer.setSize(window.innerWidth, window.innerHeight);
  labelRenderer.domElement.style.position = "absolute";
  labelRenderer.domElement.style.top = "0px";
  document.body.appendChild(labelRenderer.domElement);

  // 监听窗口尺寸改变
  window.addEventListener("resize", onWindowResize, false);
}

function addPoint() {
  raycaster = new THREE.Raycaster();
  var point_geometry = new THREE.BoxGeometry(size, 0.01, size);
  var point_material = new THREE.MeshBasicMaterial({ color: 0x0000ff });
  point = new THREE.Mesh(point_geometry, point_material);
  point.visible = false;
  point_material.transparent = true;
  point_material.opacity = 0.5;
  scene.add(point);
  window.addEventListener("mousemove", onDocumentMouseMove);
  window.addEventListener("mousedown", debounce(onDocumentMouseDown, 200));
}

function onWindowResize() {
  // 窗口尺寸改变
  try {
    (w = window.innerWidth), (h = window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    labelRenderer.setSize(window.innerWidth, window.innerHeight);
  } catch (error) {
    console.log(error);
  }
}

function createStage(models) {
  for (var i = -row / 2; i < row / 2; i++) {
    for (var j = -col / 2; j < col / 2; j++) {
      var land_clone = models.land_1.clone();
      land_clone.position.set(size * i, -0.005, size * j);
      land_clone.gameType = "land";
      land_clone.gamePoint = {
        x: i,
        z: j,
      };

      if (i == -row / 2 || i == row / 2 - 1) {
        var wall_clone = models.wall_0.clone();
        wall_clone.gameType = "wall";
        wall_clone.wallType = i == -row / 2 ? 0 : 1;
        wall_clone.gamePoint = {
          x: i,
          z: j,
        };
        wall_clone.position.set(
          size * i + (i == row / 2 - 1 ? size / 2 : -size / 2),
          0,
          size * j
        );
        group.add(wall_clone);
      }
      if (j == -col / 2 || j == col / 2 - 1) {
        var wall_clone = models.wall_0.clone();
        wall_clone.gameType = "wall";
        wall_clone.wallType = j == -col / 2 ? 0 : 1;
        wall_clone.gamePoint = {
          x: i,
          z: j,
        };
        wall_clone.rotation.y = (Math.PI / 180) * 90;
        wall_clone.position.set(
          size * i,
          0,
          size * j + (j == col / 2 - 1 ? size / 2 : -size / 2)
        );
        group.add(wall_clone);
      }

      itemsMap.forEach(function (item, index) {
        if (item.x == i && item.z == j) {
          var item_clone = models[item.type].clone();
          item_clone.material = models[item.type].material.clone();
          item_clone.gameType = item.type;
          land_clone.gameType = item.type + "_land";
          item_clone.gamePoint = {
            x: i,
            z: j,
          };
          item_clone.gameIndex = index;
          item_clone.position.set(i * size, 0, j * size);
          if (item.label) {
            addTalk(item_clone, item.name);
          }

          group.add(item_clone);
        }
      });
      group.add(land_clone);
    }
  }
}

function addLight() {
  var ambientLight = new THREE.AmbientLight(
    new THREE.Color("rgb(185,185,185)")
  );
  ambientLight.intensity = 0.9;
  scene.add(ambientLight);

  spotLight = new THREE.SpotLight(new THREE.Color("rgb(255,255,155)"));

  spotLight.position.x = 0;
  spotLight.position.y = 0.6;
  spotLight.position.z = 0;
  spotLight.intensity = 3.2;
  spotLight.castShadow = true;
  spotLight.shadow.mapSize.set(2048, 2048);
  // 从发光点发出的距离，光的亮度，会随着距离的远近线性衰减
  spotLight.distance = 2;
  // 光色散角度，默认是 Math.PI * 2
  spotLight.angle = Math.PI * 2 * 0.5;
  // 光影的减弱程度，默认值为0， 取值范围 0 -- 1之间
  spotLight.penumbra = 0.1;
  // 光在距离上的量值, 和光的强度类似（衰减指数）
  spotLight.decay = 1;
  scene.add(spotLight);

  // var helper = new THREE.SpotLightHelper(spotLight, 1);
  // scene.add(helper);

  // var shadowCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera);
  // scene.add(shadowCameraHelper);
}

function loadModel() {
  var loader = new THREE.GLTFLoader();
  loader.load(ASSETSURL + "model/stage/scene.gltf", function (gltf) {
    var items = gltf.scene.children[0].children[0].children[0].children[0];
    var land_0 = items.children[0].children[0];
    var land_1 = items.children[1].children[0];
    var land_2 = items.children[2].children[0];
    var barrel_bad = items.children[4].children[0];
    var barrel_none = items.children[5].children[0];
    var barrel_normal = items.children[7].children[0];
    var wall_0 = items.children[12].children[0];
    var wall_1 = items.children[13].children[0];
    var wall_2 = items.children[14].children[0];
    var pillar = items.children[72].children[0];
    var stone = items.children[9].children[0];
    var door = items.children[71].children[0];
    var braiser = items.children[6].children[0];
    // console.log(items);
    var models = {
      land_0: land_0,
      land_1: land_1,
      land_2: land_2,
      barrel_bad: barrel_bad,
      barrel_none: barrel_none,
      barrel_normal: barrel_normal,
      wall_0: wall_0,
      wall_1: wall_1,
      wall_2: wall_2,
      pillar: pillar,
      stone: stone,
      door: door,
      braiser: braiser,
    };
    for (var key in models) {
      models[key].scale.set(0.001, 0.001, 0.001);
      models[key].castShadow = true;
      models[key].receiveShadow = true;
    }
    createStage(models);
  });

  loader.load(ASSETSURL + "model/role/scene.gltf", function (gltf) {
    role = gltf.scene;
    addShadow(role);
    role.scale.set(0.1, 0.1, 0.1);
    role.rotation.set(0, (Math.PI / 180) * 180, 0);
    roleAnimations = gltf.animations;
    role.position.set(rolePoint.x * size, 0.018, rolePoint.z * size);
    console.log(roleAnimations);
    mixer = new THREE.AnimationMixer(role);
    mixer.addEventListener("finished", onAnimationFinished);
    actionAnimation(1);
    roleTalkTarget = role.children[0].children[0].children[0];
    addTalk(roleTalkTarget, "我是主角");
    group.add(role);
    showTalk(roleTalkTarget, "看来是出不去了，探索一下吧", "hide:2000");
  });
}

function addTalk(target, msg, y) {
  var label = document.createElement("div");
  label.className = "talk-label hide";
  label.textContent = msg || "";
  var talkLabel = new THREE.CSS2DObject(label);
  talkLabel.position.set(0, y ? y : 120, 0);
  target.add(talkLabel);
}

function showTalk(target, msg, event) {
  try {
    var element = target.children.filter(function (item) {
      return item.element;
    })[0].element;

    if (event) {
      var _event = event.split(":")[0];
      var _num = event.split(":")[1];
      if (_event == "hide") {
        setTimeout(function () {
          hideTalk(target);
        }, _num);
      }
    }
    if (element.classList.value.indexOf("hide") == -1) return;
    if (msg) {
      element.innerHTML = msg || "";
    }
    element.classList.remove("hide");
  } catch (error) {}
}

function hideTalk(target) {
  try {
    var element = target.children.filter(function (item) {
      return item.element;
    })[0].element;
    if (element.classList.value.indexOf("hide") != -1) return;
    element.classList.add("hide");
  } catch (error) {}
}

function addShadow(obj) {
  for (var i = 0; i < obj.children.length; i++) {
    var item = obj.children[i];
    item.castShadow = true;
    item.receiveShadow = true;
    if (item.children) {
      addShadow(item);
    }
  }
}

function actionAnimation(index) {
  if (action) action.stop();
  actionState = roleAnimations[index].name;
  action = mixer.clipAction(roleAnimations[index]);
  if (actionState == "skeleton-skeleton|taunt") {
    action.time = 0;
    action.loop = THREE.LoopOnce;
    action.clampWhenFinished = true;
    action.setDuration(1.5);
    action.stopWarping();
  }
  if (actionState == "skeleton-skeleton|attack") {
    action.time = 0;
    action.loop = THREE.LoopOnce;
    action.clampWhenFinished = true;
    action.setDuration(1);
    action.stopWarping();
  }
  action.play();
}

function update() {
  var delta = clock.getDelta();
  if (mixer) mixer.update(delta);
  renderer.render(scene, camera);
  labelRenderer.render(scene, camera);
  if (role) {
    camera.lookAt(role.position);
    spotLight.position.x = role.position.x;
    spotLight.position.z = role.position.z;
  }
  camera.updateMatrixWorld();
  checkIntersects();
  checkRoleToItem();
}

function checkRoleToItem() {
  if (group.children.length == 0) return;
  var _wall_obj;
  group.children.forEach(function (item) {
    if (!item.gameType) return;
    if (item.gameType == "land") return;
    if (role) {
      var dx = role.position.x / size - item.gamePoint.x;
      var dz = role.position.z / size - item.gamePoint.z;
      var d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dz, 2));
      if (item.gameType == "wall") {
        if (d < 3 && item.wallType == 1) {
          _wall_obj = item;
        }
      } else {
        if (item.children[0] && item.children[0].element) {
          if (d <= 1.3) {
            showTalk(item);
          } else {
            hideTalk(item);
          }
        }
      }
    }
  });
  if (_wall_obj) {
    _wall_obj.material.transparent = true;
    _wall_obj.material.opacity = 0.25;
  } else {
    group.children.forEach(function (item) {
      if (item.gameType != "wall") return;
      item.material.transparent = false;
      item.material.opacity = 1;
    });
  }
}

function checkIntersects() {
  raycaster.setFromCamera(mouse, camera);
  var intersects = raycaster.intersectObjects(group.children);
  intersects = intersects.filter(function (item) {
    return item.object.gameType != "wall";
  });
  if (intersects.length > 0) {
    // var targetDistance = intersects[0].distance;
    // console.log(targetDistance);

    if (SELECTITEM) {
      SELECTITEM.material.transparent = false;
      SELECTITEM.material.opacity = 1;
    }
    if (INTERSECTED != intersects[0].object) {
      if (intersects[0].object.gameType == "land") {
        SELECTITEM = null;
        INTERSECTED = intersects[0].object;
        point.visible = true;
        point.position.x = INTERSECTED.position.x;
        point.position.z = INTERSECTED.position.z;
        point.position.y = INTERSECTED.position.y + 0.02;
      } else if (intersects[0].object.gameType.indexOf("_land") == -1) {
        INTERSECTED = null;
        point.visible = false;
        SELECTITEM = intersects[0].object;
        SELECTITEM.material.transparent = true;
        SELECTITEM.material.opacity = 0.5;
      }
    }
  } else {
    point.visible = false;
    INTERSECTED = null;
    SELECTITEM = null;
  }
}

function run() {
  requestAnimationFrame(run);
  update();
}

function onDocumentMouseMove(event) {
  if (isOpenBag) return;
  event.preventDefault();
  mouse.x = (event.clientX / w) * 2 - 1;
  mouse.y = -(event.clientY / h) * 2 + 1;
}

function onAnimationFinished(e) {
  if (animatinFinishedEventList.length == 0) return;
  while (animatinFinishedEventList.length) {
    animatinFinishedEventList.pop()();
  }
}

function onDocumentMouseDown() {
  if (isOpenBag) return;
  if (INTERSECTED) {
    onInstruct({
      point: INTERSECTED.gamePoint,
      type: "move",
    });
  } else if (SELECTITEM) {
    onInstruct({
      point: SELECTITEM.gamePoint,
      index: SELECTITEM.gameIndex,
      type: "turn",
    });
    SELECTITEM = null;
  }
}

var instructEvent = {
  MOVE: onInstructMove,
  TURN: trottle(onInstructTurn, 1500),
};

function trottle(handler, delay) {
  var handler = handler || null;
  var delay = ~~delay || 1000;
  var last = 0;
  return function (e) {
    var now = +new Date();
    if (now - last > delay) {
      handler.apply(this, arguments);
      last = now;
    }
  };
}

function debounce(handler, delay) {
  var handler = handler || null;
  var delay = ~~delay || 1000;
  var timer = null;
  return function () {
    clearTimeout(timer);
    timer = setTimeout(
      function () {
        handler.apply(this, arguments);
      }.bind(this),
      delay
    );
  };
}

function onInstruct(info) {
  instructEvent[info.type.toUpperCase()](arguments[0]);
}

function onInstructTurn(info) {
  if (!itemsMap[info.index].label) {
    return;
  }
  if (isMove) return;
  if (timer) timer.pause();
  var targetX = info.point.x;
  var targetZ = info.point.z;
  var dx = role.position.x / size - targetX;
  var dz = role.position.z / size - targetZ;
  var angle = Math.atan2(dx, dz) + Math.PI;
  role.rotation.y = angle;
  isMove = false;
  var d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dz, 2));
  if (d >= 1.3) {
    if (actionState != "skeleton-skeleton|idle") {
      actionAnimation(1);
    }
    return showTalk(roleTalkTarget, "距离太远", "hide:1000");
  }
  var list = itemsMap[info.index].gift;
  actionAnimation(0);
  if (list.length == 0) {
    return showTalk(roleTalkTarget, "空空如也", "hide:1000");
  }
  animatinFinishedEventList.push(function () {
    addGift(list.pop(), targetX, 0.25, targetZ);
  });
}

function addGift(key, targetX, targetY, targetZ) {
  gift = giftData[key];
   document.querySelector(".bag-btn").classList.add("bounce");
   setTimeout(function(){
    document.querySelector(".bag-btn").classList.remove("bounce");
  },1300)
  giftList.push(gift);
  var gift_normal_map = new THREE.TextureLoader().load(
    ASSETSURL + "imgs/" + gift.pic
  );
  var gift_geometry = new THREE.BoxGeometry(size / 2, 0.01, size / 2);
  var gift_material = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    transparent: true,
    map: gift_normal_map,
  });
  var gift_mesh = new THREE.Mesh(gift_geometry, gift_material);
  gift_mesh.position.set(targetX * size, targetY, targetZ * size);
  scene.add(gift_mesh);
  timer = new gsap.timeline({
    defaults: { duration: 0 },
  });
  timer.to(
    gift_mesh.position,
    0.4,
    {
      y: 0.3,
      onComplete: function () {
        showTalk(roleTalkTarget, "获得：" + gift.name + "*1", "hide:1000");
        setTimeout(function () {
          gift_mesh.visible = false;
          scene.remove(gift_mesh);
        }, 2000);
      },
    },
    0.1
  );
  timer.to(gift_mesh.position, 0.4, { y: 0.45 }, 0.5);
  timer.to(gift_mesh.material, 0.4, { opacity: 0 }, 0.5);
}

function onInstructMove(info) {
  if (timer) timer.pause();
  if (actionState != "skeleton-skeleton|run") {
    actionAnimation(2);
  }
  var targetX = info.point.x;
  var targetZ = info.point.z;
  timer = new gsap.timeline({
    defaults: { duration: 0 },
  });
  var dx = role.position.x / size - targetX;
  var dz = role.position.z / size - targetZ;
  var d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dz, 2));
  var angle = Math.atan2(dx, dz) + Math.PI;
  role.rotation.y = angle;

  timer.to(
    role.position,
    d * speed,
    {
      x: size * targetX,
      z: size * targetZ,
      onComplete: function () {
        isMove = false;
        actionAnimation(1);
      },
    },
    0
  );
}
